#pragma once

#include <type_traits>

#include "TxDNN/txdnn.h"

#include "operators/invoker_params_base.h"
#include "operators/operator_struct.h"
#include "operators/handle.h"
#include "backend/execution_context.h"


namespace txdnn {
namespace operators {

struct OperatorBase {
public:
  OperatorBase()                    = default;
  OperatorBase(const OperatorBase&) = default;
  virtual ~OperatorBase()           = default;

  // Batch size and input size (N, H, W, C) can be determined at runtime.
  virtual bool isDynamic() const { return false; }
};

// Common interface for tunable and non-tunable solvers
template<class Context, class OperatorDesc>
struct OperatorInterface : public OperatorBase {
  static_assert(std::is_base_of<txdnn::ExecutionContext, Context>::value, "Context must be derived from ExecutionContext");

  virtual size_t getWorkspaceSize(const Context&, const OperatorDesc&) const { return 0; };
};

template<class Context, class OperatorDesc>
struct OperatorInterfaceNonTunable : public OperatorInterface<Context, OperatorDesc> {
  // Takes operation config, optimization parameters and other info
  // and computes information required to build and run the kernel(s).
  virtual Operators getOperator(const Context& ctx, const OperatorDesc& opdesc) const = 0;
};

// template<class Context, class OperatorDesc>
// struct OperatorInterfaceTunable : public OperatorInterface<Context, OperatorDesc> {
//   virtual Operators FindOperatorSimple(const Context& ctx, const OperatorDesc& opdesc,
//                                       const AnyInvokeParams& invoke_ctx) const = 0;
// };


template <class Context, class OperatorDesc>
struct OperatorBaseNonTunable : public OperatorInterfaceNonTunable<Context, OperatorDesc> {
  InvokerFactory GetInvokerFactory(const Context& ctx, const OperatorDesc& opdesc) const {
    const auto op = this->getOperator(ctx, opdesc);
    return *op.invoker_factory_;
  }
};


// struct TunableSolverTrait { };


// template<class Context, class Operator, class PerfConfig>
// struct SolverBaseTunable : public OperatorInterfaceTunable<Context, Operator>, TunableSolverTrait {
// public:
//   virtual PerfConfig GetDefaultPerfConfig(const Context& ctx, const Operator& op) const = 0;

//   virtual PerfConfig Search(const Context& ctx, const Operator& op, const AnyInvokeParams& invoke_ctx) const = 0;

//   virtual Solution GetSolution(const Context& ctx, const Operator& op, const PerfConfig& config) const =  0;

//   Solution FindSolutionSimple(const Context& ctx, const Operator& op, const AnyInvokeParams& invoke_ctx) const _TX_FINAL {
//     const PerfConfig config = Search(ctx, op, invoke_ctx);
//     return GetSolution(ctx, op, config);
//   }

//   InvokerFactory GetInvokerFactory(const Context& ctx, const Operator& op, const PerfConfig& config) const {
//     return *GetSolution(ctx, op, config).invoker_factory;
//   }
// }


template<class Context, class OperatorDesc>
using NonTunableOperatorBase = OperatorBaseNonTunable<Context, OperatorDesc>;


} // namespace operators
} // namespace txdnn